package indi.zhifa.engine.mpenhance.service.impl;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.Version;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import indi.zhifa.engine.core.common.ann.mp.CheckMin;
import indi.zhifa.engine.core.common.ann.mp.CheckUpdate;
import indi.zhifa.engine.core.common.entity.web.ServiceException;
import indi.zhifa.engine.core.common.utils.CommonUtil;
import indi.zhifa.engine.core.common.utils.DtoEntityUtil;
import indi.zhifa.engine.mpenhance.ann.ZfDbService;
import indi.zhifa.engine.mpenhance.service.IZfDbService;
import indi.zhifa.engine.mpenhance.utils.DbDtoEntityUtil;
import jakarta.annotation.PostConstruct;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;

public class ZfDbServiceImpl<M extends BaseMapper<T>, T> extends ServiceImpl<M, T> implements IZfDbService<T> {

    protected List<String> mUpdateSelectCols;
    protected List<String> mSelectMinCols;

    public ZfDbServiceImpl() {
        mUpdateSelectCols = new ArrayList<>();
        mSelectMinCols = new ArrayList<>();
    }

    @PostConstruct
    public void init(){
        Field[] fields = CommonUtil.getAllFields(this.getEntityClass());
        for (Field field : fields) {
            TableField tableField = field.getAnnotation(TableField.class);
            TableId tableId = field.getAnnotation(TableId.class);
            if (tableId != null) {
                if(StringUtils.hasText(tableId.value())){
                    mUpdateSelectCols.add(tableId.value());
                    mSelectMinCols.add(tableId.value());
                }else{
                    mUpdateSelectCols.add(StrUtil.toUnderlineCase(field.getName()));
                    mSelectMinCols.add(StrUtil.toUnderlineCase(field.getName()));
                }
            }
            Version version = field.getAnnotation(Version.class);
            CheckUpdate checkUpdate = field.getAnnotation(CheckUpdate.class);
            if (version != null || checkUpdate != null) {
                if(null==tableField||!StringUtils.hasText(tableField.value())){
                    mUpdateSelectCols.add(StrUtil.toUnderlineCase(field.getName()));
                }else{
                    mUpdateSelectCols.add(tableField.value());
                }
            }
            CheckMin checkMin = field.getAnnotation(CheckMin.class);
            if (version != null || checkMin != null) {
                if(null==tableField||!StringUtils.hasText(tableField.value())){
                    mSelectMinCols.add(StrUtil.toUnderlineCase(field.getName()));
                }else{
                    mSelectMinCols.add(tableField.value());
                }
            }
        }
    }



    @Override
    public <VO> Page<VO> page(IPage page, Wrapper<T> queryWrapper, Class<VO> cls) {
        IPage<T> pageData = super.page(page,queryWrapper);
        Page<VO> pageVoData = new Page<VO>(pageData.getCurrent(),pageData.getSize());
        pageVoData.setTotal(pageData.getTotal());
        pageVoData.setPages(pageData.getPages());
        List<VO> voList = DtoEntityUtil.trans(pageData.getRecords(),cls);
        pageVoData.setRecords(voList);
        return pageVoData;
    }

    @Override
    public <VO> Page<VO> page(IPage page, Wrapper<T> queryWrapper, Class<VO> cls, Function<T, VO> convertor) {
        IPage<T> pageData = super.page(page,queryWrapper);
        Page<VO> pageVoData = new Page<VO>(pageData.getCurrent(),pageData.getSize());
        pageVoData.setTotal(pageData.getTotal());
        pageVoData.setPages(pageData.getPages());
        List<VO> voList = new ArrayList<>(pageData.getRecords().size());
        for(T data : pageData.getRecords()){
            VO voData = convertor.apply(data);
            voList.add(voData);
        }
        pageVoData.setRecords(voList);
        return pageVoData;
    }

    @Override
    public <VO> List<VO> list(Wrapper<T> queryWrapper, Class<VO> cls) {
        List<T> listData = super.list(queryWrapper);
        List<VO> listVoData = DtoEntityUtil.trans(listData,cls);
        return listVoData;
    }

    @Override
    public <VO> List<VO> list(Wrapper<T> queryWrapper, Class<VO> cls, Function<T, VO> convertor) {
        List<T> listData = super.list(queryWrapper);
        List<VO> listVoData = new ArrayList<>();
        for(T data : listData){
            VO voData = convertor.apply(data);
            listVoData.add(voData);
        }
        return listVoData;
    }

    @Override
    public T findOne(Wrapper<T> queryWrapper) {
        List<T> listData = super.list(queryWrapper);
        if(CollectionUtils.isEmpty(listData)){
            return null;
        }
        return listData.get(0);
    }

    @Override
    public boolean existById(Serializable id) {
        Wrapper queryWrapper = Wrappers.query().eq("id", id);
        Long cnt = baseMapper.selectCount(queryWrapper);
        return cnt > 0;
    }

    @Override
    public T check(Serializable pId) {
        T entity = baseMapper.selectById(pId);
        if(null == entity){
            throw new ServiceException("没有找到Id为"+pId+"的"+getEntityName()+"数据");
        }
        return entity;
    }

    @Override
    public T checkForUpdate(Serializable pId) {
        List<String> selectStrList = new ArrayList<>();
        selectStrList.addAll(mUpdateSelectCols);
        QueryWrapper<T> queryWrapper = Wrappers.<T>query()
                .eq("id", pId)
                .select(selectStrList.stream().toList());
        T rtn = baseMapper.selectOne(queryWrapper,false);
        if(null == rtn){
            throw new ServiceException("没有找到Id为"+pId+"的"+getEntityName()+"数据");
        }
        return rtn;
    }

    @Override
    public T checkMin(Serializable pId, String... pCols) {
        List<String> selectStrList = new ArrayList<>();
        selectStrList.addAll(mSelectMinCols);
        selectStrList.addAll(Arrays.asList(pCols));
        QueryWrapper<T> queryWrapper = Wrappers.<T>query()
                .eq("id", pId)
                .select(selectStrList.stream().toList());
        T rtn = baseMapper.selectOne(queryWrapper,false);
        if(null == rtn){
            throw new ServiceException("没有找到Id为"+pId+"的"+getEntityName()+"数据");
        }
        return rtn;
    }


    @Override
    public T updatePull(Long pId, Object pEntity) {
        T rtn = checkForUpdate(pId);
        DbDtoEntityUtil.copy(rtn,pEntity);

        boolean success = super.updateById(rtn);
        if(success){
            onCacheInvalidate(rtn);
            return rtn;
        }else {
            throw new ServiceException(getEntityName()+"更新"+pId+" 失败");
        }
    }

    @Override
    public T deleteById(Serializable pId) {
        T deletingEntity = checkForUpdate(pId);
        removeById(pId);
        onCacheInvalidate(deletingEntity);
        return deletingEntity;
    }


    protected String getEntityName(){
        ZfDbService zfDbService = this.getClass().getAnnotation(ZfDbService.class);
        if(null != zfDbService){
            return zfDbService.name();
        }else {
            return this.getClass().getName();
        }
    }

    protected void onCacheInvalidate(T pEntity){

    }
}
